package com.kmxd.ams.app.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.kmxd.ams.client.archive.IArchiveTypeSvc;
import com.kmxd.ams.client.system.ISysMenuSvc;
import com.kmxd.ams.client.system.ISysRoleArchiveSvc;
import com.kmxd.ams.client.system.ISysRoleMenuSvc;
import com.kmxd.ams.client.system.ISysRoleSvc;
import com.kmxd.ams.client.system.dto.*;
import com.kmxd.ams.client.system.vo.SysMenuVO;
import com.kmxd.ams.client.system.vo.SysRoleListVO;
import com.kmxd.ams.client.system.vo.SysRoleVO;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.constant.MenuConst;
import com.kmxd.ams.core.common.enums.MenuTypeEnum;
import com.kmxd.ams.core.common.enums.StatusEnum;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.archive.entity.ArchiveType;
import com.kmxd.ams.infra.system.entity.*;
import com.kmxd.ams.infra.system.mapper.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 角色表 服务实现类
 *
 * @author admin
 * @since 2021-06-10
 */
@Service
@AllArgsConstructor
public class SysRoleSvcImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleSvc {

  private SysRoleMapper sysRoleMapper;
  private SysUserRoleMapper sysUserRoleMapper;
  private SysRoleMenuMapper sysRoleMenuMapper;
  private ISysRoleMenuSvc sysRoleMenuSvc;
  private SysDeptMapper sysDeptMapper;
  private SysMenuMapper sysMenuMapper;
  private ISysRoleArchiveSvc sysRoleArchiveSvc;
  private IArchiveTypeSvc archiveTypeSvc;
  private ISysMenuSvc sysMenuSvc;

  @Override
  public Page<SysRoleListVO> selectPage(Page reqPage, SysRoleQueryDTO req) {
    LambdaQueryWrapper<SysRole> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.like(
        StrUtil.isNotBlank(req.getRoleCode()), SysRole::getRoleCode, req.getRoleCode());
    queryWrapper.like(
        StrUtil.isNotBlank(req.getRoleName()), SysRole::getRoleName, req.getRoleName());
    queryWrapper.orderByAsc(SysRole::getSort);
    Page<SysRole> page = sysRoleMapper.selectPage(reqPage, queryWrapper);
    Page<SysRoleListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), SysRoleListVO.class));
    return voPage;
  }

  @Override
  public List<SysRoleListVO> selectList(SysRoleQueryDTO req) {
    LambdaQueryWrapper<SysRole> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.like(
        StrUtil.isNotBlank(req.getRoleCode()), SysRole::getRoleCode, req.getRoleCode());
    queryWrapper.like(
        StrUtil.isNotBlank(req.getRoleName()), SysRole::getRoleName, req.getRoleName());
    queryWrapper.orderByAsc(SysRole::getSort);
    List<SysRole> list = sysRoleMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, SysRoleListVO.class);
  }

  @Override
  public SysRole selectByCode(String code) {
    LambdaQueryWrapper<SysRole> queryWrapper = Wrappers.lambdaQuery();
    queryWrapper.like(StrUtil.isNotBlank(code), SysRole::getRoleCode, code);
    List<SysRole> roles = sysRoleMapper.selectList(queryWrapper);
    return roles.stream().findFirst().orElse(null);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(SysRoleAddDTO req) {
    if (!this.checkUniqueRoleCode(req.getRoleCode(), null)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "角色编码已存在");
    }
    SysRole entity = BeanUtil.copyProperties(req, SysRole.class);
    entity.setComId(
        ObjectUtil.isNotEmpty(req.getComId()) ? req.getComId() : SecurityUtil.getCropId());
    sysRoleMapper.insert(entity);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void edit(SysRoleEditDTO req) {
    if (!this.checkUniqueRoleCode(req.getRoleCode(), req.getId())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "角色编码已存在");
    }
    SysRole entity = BeanUtil.copyProperties(req, SysRole.class);
    entity.setComId(
        ObjectUtil.isNotEmpty(req.getComId()) ? req.getComId() : SecurityUtil.getCropId());
    sysRoleMapper.updateById(entity);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    idList.forEach(
        item -> {
          SysRole sysRole = sysRoleMapper.selectById(item);
          List<SysUserRole> sysUserRole =
              sysUserRoleMapper.selectList(
                  Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getRoleId, item));
          if (!sysUserRole.isEmpty()) {
            throw BizTipException.instance(
                ErrorCode.VALIDATE_ERROR, String.format("角色【%s】存在用户，无法删除", sysRole.getRoleName()));
          }
        });
    idList.forEach(
        item -> {
          sysRoleMapper.deleteById(item);
        });
  }

  @Override
  public SysRoleVO view(Long id) {
    SysRole entity = sysRoleMapper.selectById(id);
    return BeanUtil.copyProperties(entity, SysRoleVO.class);
  }

  @Override
  public List<SysRoleVO> listOptions() {
    List<SysRole> list =
        sysRoleMapper.selectList(
            Wrappers.lambdaQuery(SysRole.class)
                .eq(SysRole::getStatus, StatusEnum.YES.getCode())
                .orderByAsc(SysRole::getSort));
    return BeanUtil.copyToList(list, SysRoleVO.class);
  }

  @Override
  public void assignMenu(SysRoleAssignMenuDTO req) {
    // 删除角色和菜单关联
    sysRoleMenuMapper.delete(
        Wrappers.lambdaQuery(SysRoleMenu.class).eq(SysRoleMenu::getRoleId, req.getRoleId()));
    // 新增角色和菜单关联
    if (Objects.nonNull(req.getMenuIds())) {
      List<SysRoleMenu> roleMenu =
          req.getMenuIds().stream()
              .map(
                  item -> {
                    SysRoleMenu sysRoleMenu = new SysRoleMenu();
                    sysRoleMenu.setRoleId(req.getRoleId());
                    sysRoleMenu.setMenuId(item);
                    return sysRoleMenu;
                  })
              .collect(Collectors.toList());
      sysRoleMenuSvc.saveBatch(roleMenu);
    }
  }

  @Override
  public void assignMenu(List<SysRoleAssignMenuDTO> req) {
    List<Long> roleIds =
        req.stream().map(SysRoleAssignMenuDTO::getRoleId).collect(Collectors.toList());
    // 删除角色和菜单关联
    sysRoleMenuMapper.delete(
        Wrappers.lambdaQuery(SysRoleMenu.class).in(SysRoleMenu::getRoleId, roleIds));

    List<SysRoleMenu> roleMenusAll = Lists.newArrayList();
    req.forEach(
        roleMenu -> {
          if (ObjectUtil.isNotEmpty(roleMenu.getMenuIds())) {
            List<SysRoleMenu> roleMenus =
                roleMenu.getMenuIds().stream()
                    .map(
                        item -> {
                          SysRoleMenu sysRoleMenu = new SysRoleMenu();
                          sysRoleMenu.setRoleId(roleMenu.getRoleId());
                          sysRoleMenu.setMenuId(item);
                          return sysRoleMenu;
                        })
                    .collect(Collectors.toList());
            roleMenusAll.addAll(roleMenus);
          }
        });
    if (roleMenusAll.size() > 0) {
      sysRoleMenuSvc.saveBatch(roleMenusAll);
    }
  }

  @Override
  public void assignArchive(List<SysRoleAssignArchiveDTO> req) {
    List<SysRoleArchive> listSaveBatch = Lists.newArrayList();
    List<ArchiveType> list = archiveTypeSvc.list();
    req.forEach(
        raa -> {
          // 此处采用取反的方式，默认全部可看
          list.stream()
              .filter(
                  item ->
                      item.getFondsId().equals(raa.getFondsId())
                          && !raa.getArchIds().contains(item.getId()))
              .forEach(
                  arch -> {
                    SysRoleArchive archive = new SysRoleArchive();
                    archive.setId(IdWorker.getId());
                    archive.setArchId(arch.getId());
                    archive.setRoleId(raa.getRoleId());
                    archive.setFondsId(raa.getFondsId());
                    archive.setComId(SecurityUtil.getCropId());
                    archive.setRoleType(raa.getRoleType());
                    listSaveBatch.add(archive);
                  });
          // 清除已有数据
          sysRoleArchiveSvc.remove(
              Wrappers.lambdaQuery(SysRoleArchive.class)
                  .eq(SysRoleArchive::getRoleId, raa.getRoleId())
                  .eq(SysRoleArchive::getFondsId, raa.getFondsId()));
        });

    sysRoleArchiveSvc.saveBatch(listSaveBatch);
  }

  @Override
  public List<SysMenuVO> listRoleMenus(String roleId) {
    List<SysRoleMenu> sysRoleMenuList =
        sysRoleMenuMapper.selectList(
            Wrappers.lambdaQuery(SysRoleMenu.class).eq(SysRoleMenu::getRoleId, roleId));
    List<SysMenu> sysMenuList =
        sysRoleMenuList.stream()
            .map(item -> sysMenuMapper.selectById(item.getMenuId()))
            .collect(Collectors.toList());
    return BeanUtil.copyToList(sysMenuList, SysMenuVO.class);
  }

  @Override
  public Set<String> listRoleMenuIds(String roleId) {
    List<SysRoleMenu> sysRoleMenuList =
        sysRoleMenuMapper.selectList(
            Wrappers.lambdaQuery(SysRoleMenu.class).eq(SysRoleMenu::getRoleId, roleId));

    List<SysMenu> sysMenuList = sysMenuSvc.selectListAll();

    Set<Long> roleMenuIds =
        sysRoleMenuList.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toSet());

    return sysMenuList.stream()
        .filter(
            x ->
                (roleMenuIds.contains(x.getId())
                        && ObjectUtil.equals(MenuTypeEnum.BUTTON.getCode(), x.getMenuType()))
                    || (ObjectUtil.equals(x.getId(), MenuConst.DASHBOARD_ID)
                        && roleMenuIds.contains(MenuConst.DASHBOARD_ID)))
        .map(item -> String.valueOf(item.getId()))
        .collect(Collectors.toSet());
  }

  @Override
  public List<SysRole> listSysRoleByUserId(Long userId) {
    List<SysUserRole> sysUserRoleList =
        sysUserRoleMapper.selectList(
            Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
    if (sysUserRoleList.isEmpty()) {
      return new ArrayList();
    }
    List<Long> roleIdList =
        sysUserRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
    List<SysRole> sysRoleList =
        sysRoleMapper.selectList(
            Wrappers.lambdaQuery(SysRole.class)
                .in(SysRole::getId, roleIdList)
                .eq(SysRole::getStatus, StatusEnum.YES.getCode()));
    return sysRoleList;
  }

  @Override
  public List<SysRoleVO> selectOptions() {
    List<SysRole> sysRoleList =
        sysRoleMapper.selectList(
            Wrappers.lambdaQuery(SysRole.class)
                .eq(SysRole::getStatus, StatusEnum.YES.getCode())
                .eq(SysRole::getComId, SecurityUtil.getCropId())
                .orderByAsc(SysRole::getSort));
    return BeanUtil.copyToList(sysRoleList, SysRoleVO.class);
  }

  @Override
  public void roleAssignUsers(SysRoleAssignUsersDTO req) {
    // 新增角色和菜单关联
    if (Objects.nonNull(req.getUserIds())) {
      req.getUserIds()
          .forEach(
              item -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setRoleId(req.getRoleId());
                sysUserRole.setUserId(item);
                sysUserRoleMapper.insert(sysUserRole);
              });
    }
  }

  private Boolean checkUniqueRoleCode(String value, Long id) {
    if (StrUtil.isBlank(value)) {
      return true;
    }
    id = Objects.isNull(id) ? -1L : id;
    SysRole entity =
        sysRoleMapper.selectOne(
            Wrappers.lambdaQuery(SysRole.class).eq(SysRole::getRoleCode, value));
    return Objects.isNull(entity) || NumberUtil.equals(entity.getId(), id);
  }
}
